package wsi.fusion;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.xml.XmlPath;
import org.rygh.semantic.func.RdfFunctionality;
import org.rygh.semantic.gui.swing.input.Input;
import org.rygh.semantic.model.ex.PredicatePath;
import org.rygh.semantic.model.rdf.Entity;
import org.rygh.semantic.model.rdf.Predicate;

import wsi.dataguide.tree.WSXmlLeaf;
import wsi.function.Variable;
import wsi.match.MatchPair;
import wsi.match.SubtreeController;
import wsi.match.SubtreeMatch;
import wsi.subgraph.tree.EntityLeaf;
import wsi.subgraph.tree.EntityTree;

public class ProbabilityFuser extends Fuser {

	static class FusionTuple {
		Map<Variable, Entity> varMap = new HashMap<>();
	}

	private final Map<Input, FusionTuple> fusionMap = new HashMap<>();
	private FusionTuple fusionTuple;

	private EntityLeaf inputNode;
	private EntityLeaf centerNode;
	private SubtreeMatch subtreeMatch;
	private EntityTree entityTree;

	public ProbabilityFuser(SubtreeController subtreeController) {
		super(subtreeController);

	}

	public double compute(EntityLeaf node) {
		/*
		 * if the only matched predicate paths are temp paths, then we prune
		 * this.
		 */

		boolean hasNonTemp = false;

		for (MatchPair p : subtreeMatch.getMatchPairs()) {
			EntityLeaf n = p.getEntityLeaf();

			PredicatePath preds = entityTree.link(inputNode, n)
					.getPredicatePath();

			if (!preds.isTemp()) {
				hasNonTemp = true;
				break;
			}
		}

		if (!hasNonTemp)
			return 0;

		assert subtreeMatch.getXmlFunctionalityController() != null;

		subtreeMatch.setEntityTree(entityTree);
		subtreeMatch.setCenterNode(node);
		subtreeMatch.compute();

		return subtreeMatch.getProbPositiveValidMatch();
	}

	/**
	 * fuse here. ?e1, ?e2, ...
	 * 
	 * @param varEntity
	 * @param subroot
	 * @return
	 */
	private Entity fuse(Entity varEntity) {
		Entity entity = null;

		Variable variable = new Variable(varEntity);

		Predicate asso_pred = variableManager.getPredicate(variable);
		RdfFunctionality funcly = functionalityCtrl
				.getRdfFunctionality(asso_pred);

		// log.info("fuse " + varEntity.getValue() + ", " + asso_pred);

		double func = asso_pred.isInverse() ? funcly.getInverseFunctionality()
				: funcly.getFunctionality();

		PredicatePath ppath = new PredicatePath();
		ppath.add(asso_pred);

		List<EntityLeaf> children = centerNode.getChildren(ppath);

		if (children == null || children.isEmpty()) {
			// log.info(String.format(
			// "[warn] cannot find any child with predicate %s",
			// asso_pred));
			return null;
		}

		if (func > 0.95) {
			// in this case, we rely on functionality as it is good enough.

			if (children.size() > 2) {
				log.info(String
						.format("[warn] find more than one child (%d) for predicate %s",
								children.size(), asso_pred));
			}

			entity = children.get(0).getValue().getValue();
		} else {
			// we have a low functionality, thus we have to fuse

			EntityLeaf res = fuse(children);

			if (res == null) {
				log.info(String.format(
						"[warn] cannot find an entity candidate for %s",
						asso_pred));
			} else
				entity = res.getValue().getValue();
		}

		if (entity != null)
			fusionTuple.varMap.put(variable, entity);

		return entity;
	}

	@Override
	protected Entity fuse(Entity varEntity, WSXmlLeaf subroot) {
		Entity entity;
		Variable varo;
		XmlPath xpath;
		WSXmlLeaf leaf;
		String var, value = null;

		if (varEntity.isLiteral()) {
			varo = new Variable(varEntity);
			leaf = variableManager.getLeaf(varo);
			assert leaf != null : varo;

			xpath = leaf.getXpath();
			value = getValue(xpath, subroot);

			if (value == null || value.isEmpty())
				return null;

			entity = new Entity(value, true);
		} else {
			var = varEntity.getValue();

			if (var.equals(VariableManager.E_INPUT.getValue())
					|| (var.equals(VariableManager.E_CENTER.getValue()) && isInputEqualsToCenter())) {
				return inputNode.getValue().getValue();
			} else if (var.equals(VariableManager.E_CENTER.getValue())) {
				assert !isInputEqualsToCenter();
				return centerNode.getValue().getValue();
			}

			entity = fuse(varEntity);
		}

		return entity;
	}

	public EntityLeaf fuse(List<EntityLeaf> nodes) {
		EntityLeaf winner = null;
		double max = -1;

		for (EntityLeaf node : nodes) {
			double p;

			p = compute(node);

			// p = subtreeMatch.getResolutionProbability(child);
			node.setFusionProbability(p);

			if (max < p) {
				max = p;
				winner = node;
			}
		}

		return winner;
	}

	public void setCenterNode(EntityLeaf centerNode) {
		this.centerNode = centerNode;
	}

	public void setEntityTree(EntityTree entityTree) {
		this.entityTree = entityTree;
		this.inputNode = entityTree.getConcreteRoot();
	}

	@Override
	public void setInput(Input input) {
		super.setInput(input);

		fusionTuple = new FusionTuple();
		fusionMap.put(input, fusionTuple);
	}

	public void setSubtreeMatch(SubtreeMatch subtreeMatch) {
		this.subtreeMatch = subtreeMatch;
	}

	@Override
	public HtmlTable toHtmlTable() {
		TableData data = new TableData();
		TableTuple tuple = new TableTuple();

		Object[] vars = variableManager.getVarEntityInverseMap().keySet()
				.toArray();

		tuple.add("Input");

		for (Object var : vars)
			tuple.add(((Variable) var).getValue());

		data.add(tuple);

		for (Entry<Input, FusionTuple> e : fusionMap.entrySet()) {
			tuple = new TableTuple();
			tuple.add(e.getKey().getValue());

			for (Object obj : vars) {
				Variable var = (Variable) obj;
				Entity entity = e.getValue().varMap.get(var);

				tuple.add(entity == null ? "null" : entity.getValue());
			}

			data.add(tuple);
		}

		data.sort(0);

		return new HtmlTable("Fusion Results", data, false);
	}
}
